﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	ListService.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Web.Caching;

namespace Smart.Common.Entities
{
	/// <summary>
	/// List service
	/// </summary>
	public abstract class ListService<TEntityType> : IListService<TEntityType>
		where TEntityType : Entity
	{
		/// <summary>
		/// List context
		/// </summary>
		public IEntityScope Ctx { get; protected set; }

		/// <summary>
		/// List name
		/// </summary>
		public string ListName { get; protected set; }

		/// <summary>
		/// List Mapper
		/// </summary>
		public IEntityMapper Mapper { get; protected set; }

		/// <summary>
		/// List metadata
		/// </summary>
		public MetaList Meta { get; protected set; }

		/// <summary>
		/// Default entity type
		/// </summary>
		public Type DefaultEntityType { get; set; }

		/// <summary>
		/// List service
		/// </summary>
		protected ListService(IEntityScope ctx, string listName, IEntityMapper mapper, MetaList meta)
		{
			Ctx = ctx;
			ListName = listName;
			Mapper = mapper;
			Meta = meta;
			DefaultEntityType = typeof(TEntityType);
		}

		#region Data source operation

		/// <summary>
		/// Create item in data source
		/// </summary>
		/// <returns>Item</returns>
		protected abstract object CreateItem();

		/// <summary>
		/// Loads item from data source
		/// </summary>
		/// <param name="id">Item ID</param>
		/// <returns>Item</returns>
		protected abstract object LoadItem(Identifier id);

		/// <summary>
		/// Save item to data source
		/// </summary>
		/// <param name="id">Item ID</param>
		/// <param name="item">Item</param>
		/// <returns>true, if succeeded</returns>
		protected abstract bool SaveItem(Identifier id, object item, bool isNew);

		/// <summary>
		/// Recycle item (move to recycle bin) in data source
		/// </summary>
		/// <param name="id">Item ID</param>
		/// <returns>true, if succeeded</returns>
		protected abstract bool RecycleItem(Identifier id);

		/// <summary>
		/// Recycle item (move to recycle bin) in data source
		/// </summary>
		/// <param name="item">Item</param>
		/// <returns>true, if succeeded</returns>
		protected abstract bool RecycleItem(object item);

		/// <summary>
		/// Loads items from data source using query
		/// </summary>
		/// <param name="query">Data source query</param>
		/// <param name="rowLimit">Row limit</param>
		/// <param name="properties">Query additional properties</param>
		/// <returns>Items</returns>
		protected abstract IEnumerable LoadItems(string query, int rowLimit, Dictionary<string, object> properties);

		/// <summary>
		/// Detects entity name from data source item
		/// </summary>
		/// <param name="item">Item</param>
		/// <returns>Entity information</returns>
		public abstract EntityInfo DetectEntityInfo(object item);

		/// <summary>
		/// Return item identifier
		/// </summary>
		/// <param name="item">Item</param>
		protected abstract Identifier GetId(object item);

		#endregion

		#region Entity operation

		/// <summary>
		/// Create entity
		/// </summary>
		public virtual TEntityType CreateEntity(string entityName, IEntityList list)
		{
			return (TEntityType)EntityFactory.CreateEntity(entityName, list, Mapper);
		}

		/// <summary>
		/// Create entity
		/// </summary>
		public virtual TEntityType CreateEntity(Type entityType, IEntityList list)
		{
			return (TEntityType)EntityFactory.CreateEntity(entityType, list, Mapper);
		}

		/// <summary>
		/// Create entity
		/// </summary>
		public virtual TEntityType CreateEntity(IEntityList list)
		{
			return (TEntityType)EntityFactory.CreateEntity(DefaultEntityType, list, Mapper);
		}

		/// <summary>
		/// Fill entity default values
		/// </summary>
		/// <param name="entity">Entity to fill</param>
		/// <returns>true, if succeeded</returns>
		public bool FillEntity(TEntityType entity)
		{
			if (entity == null)
				throw new ArgumentNullException();

			return EntityFactory.FillEntity(entity, Mapper);
		}

		/// <summary>
		/// Load entity values using item (loads item by Entity.ID)
		/// </summary>
		/// <param name="entity">Entity to load</param>
		/// <returns>true, if succeeded</returns>
		public virtual bool LoadEntity(TEntityType entity)
		{
			if (entity == null)
				throw new ArgumentNullException();

			if (entity.IsNew)
				return true;

			var item = entity.GetSource();

			return item != null && LoadEntity(entity, item);
		}

		/// <summary>
		/// Load entity source
		/// </summary>
		public virtual object LoadEntitySource(TEntityType entity)
		{
			return LoadItem(entity.ID);
		}

		/// <summary>
		/// Save entity values in item (loads item by Entity.ID)
		/// </summary>
		/// <param name="entity">Entity to save</param>
		/// <returns>true, if succeeded</returns>
		public virtual bool SaveEntity(TEntityType entity)
		{
			if (entity == null)
				throw new ArgumentNullException();

			var item = (entity.IsNew ? CreateItem() : entity.GetSource());

			return item != null && SaveEntity(item, entity);
		}

		/// <summary>
		/// Recycles entity, uses recycling by entity.ID
		/// </summary>
		/// <param name="entity">Entity to recycle</param>
		/// <returns>true, if succeeded</returns>
		public virtual bool RecycleEntity(TEntityType entity)
		{
			if (entity == null)
				throw new ArgumentNullException();

			return entity.IsNew || RecycleItem(entity.ID);
		}

		/// <summary>
		/// Load entity values using item
		/// </summary>
		/// <param name="entity">Entity to load to</param>
		/// <param name="item">Item to load from</param>
		/// <returns>true, if succeeded</returns>
		public virtual bool LoadEntity(TEntityType entity, object item)
		{
			if (entity == null || item == null)
				throw new ArgumentNullException();

			return EntityFactory.LoadEntity(entity, item, Mapper);
		}

		/// <summary>
		/// Save entity values in item
		/// </summary>
		/// <param name="item">Item to save to</param>
		/// <param name="entity">Entity to save from</param>
		/// <returns>true, if succeeded</returns>
		public virtual bool SaveEntity(object item, TEntityType entity)
		{
			if (entity == null || item == null)
				throw new ArgumentNullException();

			return
				EntityFactory.SaveEntity(item, entity, Mapper)
				&& SaveItem(entity.ID, item, entity.IsNew)
				&& LoadEntity(entity, item);
		}

		/// <summary>
		/// Recycles entity, uses recycling by item
		/// </summary>
		/// <param name="entity">Entity to recycle</param>
		/// <param name="item">Item to recycle</param>
		/// <returns>true, if succeeded</returns>
		public virtual bool RecycleEntity(TEntityType entity, object item)
		{
			if (item == null)
				throw new ArgumentNullException();

			return entity.IsNew || RecycleItem(item);
		}

		/// <summary>
		/// Loads items using query
		/// </summary>
		/// <param name="query">Data source query</param>
		/// <param name="rowLimit">Row limit</param>
		/// <returns>Items</returns>
		public virtual IEntityList<TEntityType> LoadEntities(string query, int rowLimit, Dictionary<string, object> properties)
		{
			var source = LoadItems(query, rowLimit, properties);

			if (source == null)
				throw new InvalidDataException();

			var list = new EntityList<TEntityType>(Meta, this);
			var ilist = (IEntityList)list;

			foreach (var item in source)
			{
				var ei = DetectEntityInfo(item);

				if (ei == null)
					continue;

				var entity = CreateEntity(ei.Type, ilist);

				entity.ID = GetId(item);

				if (LoadEntity(entity, item))
				{
					list.Add(entity);
				}
			}

			return list;
		}

		/// <summary>
		/// Loads items using query
		/// </summary>
		/// <param name="query">Data source query</param>
		/// <param name="rowLimit">Row limit</param>
		/// <param name="facadeName">Cache key</param>
		/// <param name="cacheUserDependent">Cache user dependent</param>
		/// <param name="cacheUrlDependent">Cache url dependent</param>
		/// <param name="cacheAbsoluteTimeout">Cache absolute timeout</param>
		/// <param name="cacheSlidingExpiration">Cache sliding expiration</param>
		/// <param name="cachePriority">Cache item priority</param>
		/// <returns>Items</returns>
		public virtual IEntityList<TEntityType> LoadEntities(string query, int rowLimit, Dictionary<string, object> properties, string facadeName, bool cacheUserDependent,
															 bool cacheUrlDependent, TimeSpan cacheAbsoluteTimeout, TimeSpan cacheSlidingExpiration, CacheItemPriority cachePriority)
		{
			return
				ListServiceCache.LoadEntities(Meta, this,
											  query, rowLimit, properties, facadeName, cacheUserDependent, cacheUrlDependent,
											  cacheAbsoluteTimeout, cacheSlidingExpiration, cachePriority,
											  () => LoadEntities(query, rowLimit, properties));
		}

		#endregion
	}
}